package com.judge.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.api.domain.UserExeResult;
import com.api.domain.dto.JudgeSubmitDTO;
import com.api.domain.vo.UserQuestionResultVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.common.core.constants.Constants;
import com.common.core.constants.JudgeConstants;
import com.common.core.enums.CodeRunStatus;
import com.judge.domain.SandBoxExecuteResult;
import com.judge.domain.UserSubmit;
import com.judge.mapper.UserSubmitMapper;
import com.judge.service.JudgeService;
import com.judge.service.SandboxPoolService;
import com.judge.service.SandboxService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson2.JSON;

import java.util.ArrayList;
import java.util.List;

@Service
public class JudgeServiceImpl implements JudgeService {

    @Resource
    SandboxService sandboxService;

    @Resource
    SandboxPoolService sandboxPoolService;

    @Resource
    UserSubmitMapper userSubmitMapper;

    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        SandBoxExecuteResult sandBoxExecuteResult=
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(),judgeSubmitDTO.getUserCode(),judgeSubmitDTO.getInputList());

        UserQuestionResultVO userQuestionResultVO=new UserQuestionResultVO();

        if(sandBoxExecuteResult!=null && CodeRunStatus.SUCCEED.equals(sandBoxExecuteResult.getRunStatus())){
            //运行成功
            userQuestionResultVO=doJudge(sandBoxExecuteResult,judgeSubmitDTO,userQuestionResultVO);
        }else{
            //运行失败
            userQuestionResultVO.setPass(Constants.FALSE);
            if(sandBoxExecuteResult!=null){
                userQuestionResultVO.setExeMessage(CodeRunStatus.COMPILE_FAILED.getMsg());
            }
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
        }

        //存入数据库
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);

        return userQuestionResultVO;
    }

    private UserQuestionResultVO doJudge(SandBoxExecuteResult sandBoxExecuteResult,JudgeSubmitDTO judgeSubmitDTO,UserQuestionResultVO userQuestionResultVO){
        //比较输出结果，和正确结果
        List<String> exeOutputList = sandBoxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();
        if(exeOutputList.size()!=outputList.size()){
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }
        List<UserExeResult> list=new ArrayList<>();
        boolean flag = resultCompare(judgeSubmitDTO, exeOutputList, outputList, list);
        return assembleUserQuestionResultVO(sandBoxExecuteResult, judgeSubmitDTO, userQuestionResultVO, list, flag);
    }

    private static UserQuestionResultVO assembleUserQuestionResultVO(SandBoxExecuteResult sandBoxExecuteResult, JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO, List<UserExeResult> list, boolean flag) {
        userQuestionResultVO.setUserExeResultList(list);
        if(!flag){
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());
            return userQuestionResultVO;
        }
        //1.比较时间限制
        if(sandBoxExecuteResult.getUseTime()> judgeSubmitDTO.getTimeLimit()){
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());
            return userQuestionResultVO;
        }
        //2.比较空间限制
        if(sandBoxExecuteResult.getUseMemory()> judgeSubmitDTO.getSpaceLimit()){
            userQuestionResultVO.setPass(Constants.FALSE);
            userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);
            userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());
            return userQuestionResultVO;
        }

        //题目通过
        userQuestionResultVO.setPass(Constants.TRUE);
        Integer score= judgeSubmitDTO.getDifficulty() * JudgeConstants.DEFAULT_SCORE;
        userQuestionResultVO.setScore(score);
        return userQuestionResultVO;
    }

    private static boolean resultCompare(JudgeSubmitDTO judgeSubmitDTO, List<String> exeOutputList, List<String> outputList, List<UserExeResult> list) {
        boolean flag=true;
        for(int i = 0; i< exeOutputList.size(); i++){
            String exeOutput = exeOutputList.get(i);
            String output = outputList.get(i);
            String input = judgeSubmitDTO.getInputList().get(i);

            UserExeResult userExeResult=new UserExeResult();
            userExeResult.setExeOutput(exeOutput);
            userExeResult.setInput(input);
            userExeResult.setOutput(output);
            list.add(userExeResult);

            if(!exeOutput.equals(output)){
                flag=false;
            }
        }
        return flag;
    }

    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        //存入数据库
        UserSubmit userSubmit = new UserSubmit();
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);

        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setProgramType(judgeSubmitDTO.getProgramType());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());

        //存入数据库前先删除原来数据库有的记录
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                .eq(judgeSubmitDTO.getExamId() != null, UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        userSubmitMapper.insert(userSubmit);
    }
}
